Виджеты. Stack и Positioned
➡️Ссылка на репозиторий с кодом этого урока
Подготовка
- В директорию
lib/stateful_widgetsдобавим новый файл с названиемs4_stack_widget.dart - ⭕ Добавим в файл
main.dartвиджетStackExample()
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Flutter Course",
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
),
home: Scaffold(
body: StackExample()
),
);
}
}
Вёрстка слоями
Виджеты Stack и Positioned - позволяют размещать виджеты друг над другом, создавая эффекты наложения, значков уведомлений (бейджей), водяных знаков и многого другого.
В Column и Row, виджеты располагаются последовательно. Но что, если вам нужно разместить один виджет поверх другого? Например, текст на фоне изображения, иконку поверх аватара или значок "Новинка" на углу карточки товара? Именно для таких задач и предназначен Stack.
Виджет Stack
Это виджет-контейнер, который позволяет размещать список дочерних виджетов друг над другом.
Дочерние виджеты располагаются в порядке их следования в списке children: первый виджет будет находиться в самом низу стека, а последний — в самом верху.
По умолчанию, дочерние виджеты в Stack выравниваются в верхнем левом углу Stack и пытаются занять все доступное пространство, если их размер не ограничен.
Параметры виджета Stack:
children: Список виджетов, которые будут размещены в стеке.alignment: Определяет, как дочерние виджеты будут выравниваться в Stack, если их размер меньше размера Stack. По умолчаниюAlignmentDirectional.topStart(верхний левый угол).fit: Определяет, как дочерние виджеты, не обернутые вPositioned, будут подстраиваться под размеры Stack. По умолчаниюStackFit.loose(дочерние виджеты могут быть меньше Stack).StackFit.expandзаставит их расшириться, чтобы заполнить Stackoverflow: Определяет, как обрабатываются дочерние виджеты, выходящие за границы Stack. По умолчаниюOverflow.clip(обрезаются).
Файл s4_stack_widget.dart
class StackExample extends StatelessWidget {
const StackExample({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
// Первый виджет (внизу)
Container(
width: 300,
height: 300,
color: Colors.red[200],
),
// Второй виджет (выше первого)
Container(
width: 250,
height: 250,
color: Colors.green[200],
),
// Третий виджет (выше второго)
Container(
width: 200,
height: 200,
color: Colors.blue[300],
),
],
);
}
}

В этом примере три контейнера накладываются друг на друга, причем зеленый поверх красного, а синий поверх зеленого, так как они идут в таком порядке в списке children. Все они выравниваются по верхнему левому углу по умолчанию.
Теперь заменим контейнеры и сделаем выравнивание по центру, получится интересный эффект (как круговые диаграммы или цель для игры в darts)
Файл s4_stack_widget.dart
class StackExample extends StatelessWidget {
const StackExample({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
// Первый виджет (внизу)
Center(
child: CircleAvatar(radius: 200, backgroundColor: Colors.red[200]),
),
// Второй виджет (выше первого)
Center(
child: CircleAvatar(radius: 150, backgroundColor: Colors.green[200]),
),
// Третий виджет (выше второго)
Center(
child: CircleAvatar(radius: 100, backgroundColor: Colors.blue[300]),
),
],
);
}
}

Виджет Positioned
Это специальный виджет, который используется только как дочерний элемент виджета Stack. Он не является самостоятельным виджетом. Positioned оборачивает другой виджет и позволяет точно указать его положение и размер относительно границ родительского Stack.
Без Positioned, дочерние виджеты в Stack просто выравниваются согласно параметру alignment Stack и, возможно, расширяются согласно fit.
Positioned дает полный контроль над положением.
Ключевые параметры виджета Positioned:
child: Виджет, который вы хотите позиционировать внутри Stack.top: Расстояние от верхней границы Stack до верхней границы дочернего виджета.bottom: Расстояние от нижней границы Stack до нижней границы дочернего виджета.left: Расстояние от левой границы Stack до левой границы дочернего виджета.right: Расстояние от правой границы Stack до правой границы дочернего виджета.width: Явно заданная ширина дочернего виджета.height: Явно заданная высота дочернего виджета.
Важное замечание о параметрах Positioned:
- Если вы задаете
leftиright,Positionedигнорируетwidthи пытается определить ширину дочернего виджета так, чтобы он растянулся отleftдоright. - Аналогично, если вы задаете
topиbottom,Positionedигнорируетheightи пытается определить высоту дочернего виджета так, чтобы он растянулся отtopдоbottom. - Если вы задаете только
left(илиright) и/или толькоtop(илиbottom), а такжеwidthиheight, виджет будет иметь заданные размеры и будет позиционирован относительно указанных границ.
Пример Positioned внутри Stack
В этом примере мы создаем Stack, который содержит два дочерних виджета: Container и Positioned. Синий Container заполняет весь Stack (поскольку он первый и не обернут в Positioned), а Positioned размещает красный Container в правом нижнем углу с отступами 10 единиц.
Stack(
children: [
Container(
color: Colors.blue[],
),
Positioned(
bottom: 10, // Отступ 10 от нижней границы Stack
right: 10, // Отступ 10 от правой границы Stack
child: Container(
color: Colors.red,
width: 50,
height: 50,
),
),
],
)
